package com.ai.somatosensoryControl;

import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import com.fazecast.jSerialComm.SerialPort;
import java.util.*;
import org.opencv.videoio.Videoio;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 手势控制
 *
 * 硬件配置：
 *  支持USB摄像头（分辨率至少640x480）
 *  机器人控制器需支持串口通信（常见波特率9600）
 *
 *
 *  手势识别原理：
 *
 * 图像预处理：灰度转换 → 高斯模糊 → 阈值分割
 *
 * 特征提取：轮廓分析 → 凸包检测 → 凸性缺陷计算
 *
 * 手指计数：通过凸性缺陷深度判断手指数量
 *
 * 手势映射：
 *
 * 张开手掌（5指）→ 前进
 *
 * 四指 → 后退
 *
 * 三指 → 右转
 *
 * 二指 → 左转
 *
 * 握拳 → 停止
 *
 * 此完整实现可直接编译运行，但需注意：
 * 根据实际硬件修改串口名称和波特率
 *
 * 调整光线环境确保手部清晰可见
 *
 * 可能需要根据具体机器人协议修改指令字符（代码中"F/B/L/R/S"）
 *
 * OpenCV本地库配置需正确
 *
 */
public class FullGestureControl {

    // 串口通信相关
    private static final String PORT_NAME = "COM3";
    private static final int BAUD_RATE = 9600;

    // 加载OpenCV库
    static {
        // 这里将openCV的dll 文件配置在 D:\InstallationenvironmentHwp\jdk17\bin\opencv_java470.dll 下存放
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // 自动加载 OpenCV 的核心库
    }

    /**
     *  主函数
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // 启动程序
        new FullGestureControl().run();
    }

    /**
     * 主函数
     *
     * 摄像头-> 识别手势-> 串口发送指令-> 机器人移动
     */
    public void run() {

        // 创建摄像头控制器，并使用 try-with-resources 自动关闭
        try (CameraController camera = new CameraController()) {
            // 创建手势处理器
            GestureProcessor processor = new GestureProcessor();
            System.out.println("系统启动成功 - 开始手势识别");
            String lastGesture = "NONE"; // 记录上一次手势，避免重复输出
            while (true) {
                // 捕获摄像头图像
                Mat frame = camera.captureFrame();
                if (frame.empty()) continue;
                // 处理图像并返回手势
                String gesture = processor.processFrame(frame);
                // 只在手势发生变化时打印
                if (!gesture.equals(lastGesture) && !"NONE".equals(gesture)) {
                    String shoushizhongwen = shoushizhongwen(gesture);
                    System.out.println("识别的手势: " + shoushizhongwen + " (原始识别: " + gesture + ")");
                    lastGesture = gesture; // 更新手势记录
                }
                // 显示调试窗口
                Mat displayFrame = processor.getDebugFrame();
                HighGui.imshow("Gesture Control", displayFrame);
                // 降低帧率（500 毫秒检测一次）
                Thread.sleep(50);
                // ESC键退出
                if (HighGui.waitKey(1) == 27) break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            HighGui.destroyAllWindows();
        }
    }



    /**
     * 摄像头控制器（带自动关闭）
     */
    static class CameraController implements AutoCloseable {

        // 摄像头控制器
        private final VideoCapture cap;

        /**
         * 构造函数，初始化摄像头控制器
         */
        public CameraController() {
            // 加载OpenCV库
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
            // 创建摄像头控制器
            cap = new VideoCapture(0);
            // 检查摄像头是否打开
            if (!cap.isOpened()) {
                throw new RuntimeException("无法打开摄像头");
            }
            // 设置摄像头参数
            cap.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
            cap.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
        }

        /**
         * 捕获一帧图像
         * @return
         */
        public Mat captureFrame() {
            // 创建Mat对象，用于存储捕获的图像
            Mat frame = new Mat();
            // 从摄像头捕获一帧图像
            cap.read(frame);
            return frame;
        }

        /**
         * 关闭摄像头控制器
         */
        @Override
        public void close() {
            // 释放摄像头资源
            cap.release();
        }
    }

    /**
     * 手势处理核心 （重点）
     */
    static class GestureProcessor {

        private Mat debugFrame;

        private static final double MIN_CONTOUR_AREA = 10000; // 增大面积阈值
        private static final double ANGLE_THRESHOLD = 80;     // 角度阈值优化
        private static final double DEPTH_THRESHOLD = 25;     // 深度阈值优化
        private long lastProcessTime = 0;
        private final Queue<Integer> fingerCountQueue = new LinkedList<>(); // 用于滤波的队列

        /**
         * 处理一帧图像，返回手势
         * @param input  输入图像
         * @return
         */
        public String processFrame(Mat input) {
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastProcessTime < 33) { // 保持约30fps
                return "NONE";
            }
            lastProcessTime = currentTime;

            debugFrame = input.clone();
            Mat processed = new Mat();

            Mat resized = null;
            Mat hsv = null;
            try {
                // 1. 预处理优化（尺寸缩小加速处理）
                resized = new Mat();
                Imgproc.resize(input, resized, new Size(320, 240));

                // 2. 使用HSV颜色空间更好分离皮肤区域
                hsv = new Mat();
                Imgproc.cvtColor(resized, hsv, Imgproc.COLOR_BGR2HSV);
                Core.inRange(hsv, new Scalar(0, 30, 60), new Scalar(30, 150, 255), processed);

                // 3. 形态学操作优化
                Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5, 5));
                Imgproc.morphologyEx(processed, processed, Imgproc.MORPH_CLOSE, kernel);
                Imgproc.morphologyEx(processed, processed, Imgproc.MORPH_OPEN, kernel);

                // 4. 轮廓分析增强
                List<MatOfPoint> contours = new ArrayList<>();
                Mat hierarchy = new Mat();
                Imgproc.findContours(processed, contours, hierarchy,
                        Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

                if (contours.isEmpty()) return "NONE";

                // 5. 找到有效手部轮廓
                MatOfPoint maxContour = contours.stream()
                        .filter(c -> Imgproc.contourArea(c) > MIN_CONTOUR_AREA)
                        .max(Comparator.comparingDouble(Imgproc::contourArea))
                        .orElse(null);

                if (maxContour == null) return "NONE";

                // 6. 凸缺陷分析优化
                MatOfInt hull = new MatOfInt();
                Imgproc.convexHull(maxContour, hull);

                MatOfInt4 defects = new MatOfInt4();
                if (hull.rows() > 3) {
                    Imgproc.convexityDefects(maxContour, hull, defects);
                }

                // 7. 精确手指计数
                int fingerCount = calculateFingerCount(maxContour, defects);

                // 使用滑动窗口滤波（取3次检测结果的中位数）
                fingerCountQueue.add(fingerCount);
                if (fingerCountQueue.size() > 3) {
                    fingerCountQueue.poll();
                }
                fingerCount = getMedian(fingerCountQueue);

                // 8. 绘制调试信息
                //drawDebugInfo(resized, maxContour, hull, fingerCount);

                return classifyGesture(fingerCount);
            } finally {
                // 释放所有中间Mat对象
                resized.release();
                hsv.release();
                processed.release();
            }
        }

        /**
         * 获取手指数量
         * @param contour  轮廓点
         * @param defects  凸缺陷
         * @return
         */
        private int calculateFingerCount(MatOfPoint contour, MatOfInt4 defects) {
            if (defects.empty()) return 0;

            Point[] contourArray = contour.toArray();
            int[] defectsArray = defects.toArray();
            int validDefects = 0;

            for (int i = 0; i < defectsArray.length; i += 4) {
                int startIdx = defectsArray[i];
                int endIdx = defectsArray[i+1];
                int farIdx = defectsArray[i+2];
                float depth = defectsArray[i+3]/256f;

                Point start = contourArray[startIdx];
                Point end = contourArray[endIdx];
                Point far = contourArray[farIdx];

                // 向量法计算角度
                double a = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
                double b = Math.sqrt(Math.pow(far.x - start.x, 2) + Math.pow(far.y - start.y, 2));
                double c = Math.sqrt(Math.pow(end.x - far.x, 2) + Math.pow(end.y - far.y, 2));
                double angle = Math.toDegrees(Math.acos((b*b + c*c - a*a)/(2*b*c)));

                if (angle < ANGLE_THRESHOLD && depth > DEPTH_THRESHOLD) {
                    validDefects++;
                }
            }
            return validDefects + 1; // 缺陷数+1=手指数量
        }

        /**
         * 中位数滤波
         * @param queue  待处理队列
         * @return
         */
        private int getMedian(Queue<Integer> queue) {
            return queue.stream()
                    .sorted()
                    .skip(queue.size()/2)
                    .findFirst()
                    .orElse(0);
        }

        /**
         * 手势分类
         * @param fingers  手指数量
         * @return
         */
        private String classifyGesture(int fingers) {
            return switch (fingers) {
                case 0, 1 -> "STOP";  // 握拳或单指
                case 2 -> "MOVE_LEFT";
                case 3 -> "MOVE_RIGHT";
                case 4 -> "MOVE_BACK";
                case 5 -> "MOVE_FORWARD";
                default -> "UNKNOWN";
            };
        }

        // 获取调试帧
        public Mat getDebugFrame() {
            return debugFrame;
        }

    }

    /**
     * 机器人控制器
     */
    static class RobotController {

        // 串口通信实现
        private final SerialCommunicator serial;

        // 构造函数
        public RobotController(SerialCommunicator serial) {
            this.serial = serial;
        }

        /**
         * 执行手势控制命令
         * @param gesture
         */
        public void executeCommand(String gesture) {
            String command = switch (gesture) {
                case "MOVE_FORWARD" -> "F";// 前进
                case "MOVE_BACK" -> "B"; // 后退
                case "MOVE_LEFT" -> "L"; // 左转
                case "MOVE_RIGHT" -> "R"; // 右转
                case "STOP" -> "S"; // 停止
                default -> "";
            };

            if (!command.isEmpty()) {
                // 发送控制指令
                serial.send(command);
                //System.out.println("执行指令: " + command);
            }
        }
    }

    /**
     * 串口通信实现
     */
    static class SerialCommunicator implements AutoCloseable {

        // 串口对象
        private SerialPort serialPort;

        /**
         * 构造函数
         * @param portName  串口名称
         * @param baudRate  波特率
         * @throws Exception
         */
        public SerialCommunicator(String portName, int baudRate) throws Exception {
            // 获取所有可用串口
            SerialPort[] ports = SerialPort.getCommPorts();
            if (ports.length == 0) {
                throw new RuntimeException("未检测到任何可用串口");
            }

            // 查找目标串口
            serialPort = Arrays.stream(ports)
                    .filter(p -> p.getSystemPortName().equalsIgnoreCase(portName))
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("未找到串口: " + portName));

            // 配置串口参数
            serialPort.setBaudRate(baudRate);
            serialPort.setNumDataBits(8);
            serialPort.setNumStopBits(1);
            serialPort.setParity(SerialPort.NO_PARITY);
            serialPort.setComPortTimeouts(
                    SerialPort.TIMEOUT_WRITE_BLOCKING,
                    1000, // 写入超时
                    0     // 读取超时（不等待）
            );

            // 尝试打开端口
            if (!serialPort.openPort()) {
                throw new RuntimeException("打开串口失败，可能原因：" +
                        "\n1. 端口被其他程序占用" +
                        "\n2. 驱动程序未正确安装" +
                        "\n3. 权限不足（Linux/Mac需sudo运行）");
            }
            System.out.println("成功打开串口: " + portName);
        }


        /**
         * 发送数据
         * @param data
         */
        public void send(String data) {
            serialPort.writeBytes(data.getBytes(), data.length());
        }

        /**
         * 关闭串口
         */
        @Override
        public void close() {
            if (serialPort != null) {
                serialPort.closePort();
            }
        }
    }

    /**
     * 测试
     * 手势映射：
     *  张开手掌（5指）→ 前进
     *  四指 → 后退
     *  三指 → 右转
     *  二指 → 左转
     *  握拳或单指 → 停止
     * @param gesture
     * @return
     */
    public String shoushizhongwen(String gesture){
        String command = switch (gesture) {
            case "MOVE_FORWARD" -> "前进";
            case "MOVE_BACK" -> "后退";
            case "MOVE_LEFT" -> "左转";
            case "MOVE_RIGHT" -> "右转";
            case "STOP" -> "停止";
            default -> "";
        };
        return command;
    }

}















































//package com.ai.somatosensoryControl;
//
//import org.opencv.core.*;
//import org.opencv.highgui.HighGui;
//import org.opencv.imgproc.Imgproc;
//import org.opencv.videoio.VideoCapture;
//import com.fazecast.jSerialComm.SerialPort;
//
//import java.util.*;
//
//import org.opencv.videoio.Videoio;
//import java.util.LinkedList;
//import java.util.Queue;
//
///**
// * 手势控制
// *
// * 硬件配置：
// *  支持USB摄像头（分辨率至少640x480）
// *  机器人控制器需支持串口通信（常见波特率9600）
// *
// *
// *  手势识别原理：
// *
// * 图像预处理：灰度转换 → 高斯模糊 → 阈值分割
// *
// * 特征提取：轮廓分析 → 凸包检测 → 凸性缺陷计算
// *
// * 手指计数：通过凸性缺陷深度判断手指数量
// *
// * 手势映射：
// *
// * 张开手掌（5指）→ 前进
// *
// * 四指 → 后退
// *
// * 三指 → 右转
// *
// * 二指 → 左转
// *
// * 握拳 → 停止
// *
// * 此完整实现可直接编译运行，但需注意：
// * 根据实际硬件修改串口名称和波特率
// *
// * 调整光线环境确保手部清晰可见
// *
// * 可能需要根据具体机器人协议修改指令字符（代码中"F/B/L/R/S"）
// *
// * OpenCV本地库配置需正确
// *
// */
//public class FullGestureControl {
//
//    // 串口通信相关
//    private static final String PORT_NAME = "COM3";
//    private static final int BAUD_RATE = 9600;
//
//    // 加载OpenCV库
//    static {
//        // 这里将openCV的dll 文件配置在 D:\InstallationenvironmentHwp\jdk17\bin\opencv_java470.dll 下存放
//        System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // 自动加载 OpenCV 的核心库
//    }
//
//    /**
//     *  主函数
//     * @param args
//     * @throws Exception
//     */
//    public static void main(String[] args) throws Exception {
//        // 启动程序
//        new FullGestureControl().run();
//    }
//
//    /**
//     * 主函数
//     *
//     * 摄像头-> 识别手势-> 串口发送指令-> 机器人移动
//     */
//    public void run() {
//
//        // 创建摄像头控制器，并使用 try-with-resources 自动关闭
//        try (CameraController camera = new CameraController()) {
//
//            // 创建手势处理器
//            GestureProcessor processor = new GestureProcessor();
//            System.out.println("系统启动成功 - 开始手势识别");
//
//            String lastGesture = "NONE"; // 记录上一次手势，避免重复输出
//
//            while (true) {
//                // 捕获摄像头图像
//                Mat frame = camera.captureFrame();
//                if (frame.empty()) continue;
//
//                // 处理图像并返回手势
//                String gesture = processor.processFrame(frame);
//
//                // 只在手势发生变化时打印
//                if (!gesture.equals(lastGesture) && !"NONE".equals(gesture)) {
//                    String shoushizhongwen = shoushizhongwen(gesture);
//                    System.out.println("识别的手势: " + shoushizhongwen + " (原始识别: " + gesture + ")");
//                    lastGesture = gesture; // 更新手势记录
//                }
//
//                // 显示调试窗口
//                Mat displayFrame = processor.getDebugFrame();
//                HighGui.imshow("Gesture Control", displayFrame);
//
//                // 降低帧率（500 毫秒检测一次）
//                Thread.sleep(500);
//
//                // ESC键退出
//                if (HighGui.waitKey(1) == 27) break;
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            HighGui.destroyAllWindows();
//        }
//    }
//
//
//
//    /**
//     * 摄像头控制器（带自动关闭）
//     */
//    static class CameraController implements AutoCloseable {
//
//        // 摄像头控制器
//        private final VideoCapture cap;
//
//        /**
//         * 构造函数，初始化摄像头控制器
//         */
//        public CameraController() {
//            // 加载OpenCV库
//            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//            // 创建摄像头控制器
//            cap = new VideoCapture(0);
//            // 检查摄像头是否打开
//            if (!cap.isOpened()) {
//                throw new RuntimeException("无法打开摄像头");
//            }
//            // 设置摄像头参数
//            cap.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
//            cap.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
//        }
//
//        /**
//         * 捕获一帧图像
//         * @return
//         */
//        public Mat captureFrame() {
//            // 创建Mat对象，用于存储捕获的图像
//            Mat frame = new Mat();
//            // 从摄像头捕获一帧图像
//            cap.read(frame);
//            return frame;
//        }
//
//        /**
//         * 关闭摄像头控制器
//         */
//        @Override
//        public void close() {
//            // 释放摄像头资源
//            cap.release();
//        }
//    }
//
//    /**
//     * 手势处理核心 （重点）
//     */
//    // 修改后的 GestureProcessor 类
//    static class GestureProcessor {
//
//        private Mat debugFrame;
//
//        /**
//         * 处理一帧图像，返回手势
//         * @param input  输入图像
//         * @return
//         */
//        public String processFrame(Mat input) {
//            debugFrame = input.clone();
//
//            // 1. 预处理 - 转换为灰度图像并模糊
//            Mat gray = new Mat();
//            Imgproc.cvtColor(input, gray, Imgproc.COLOR_BGR2GRAY);
//            Imgproc.GaussianBlur(gray, gray, new Size(5, 5), 0);
//
//            // 2. 手部区域分割 - 二值化
//            Mat threshold = new Mat();
//            Imgproc.threshold(gray, threshold, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
//
//            // 3. 轮廓分析 - 寻找轮廓
//            List<MatOfPoint> contours = new ArrayList<>();
//            Mat hierarchy = new Mat();
//            Imgproc.findContours(threshold, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
//
//            // 如果没有找到有效轮廓，返回 "NONE"
//            if (contours.isEmpty()) return "NONE";
//
//            // 找到最大轮廓
//            MatOfPoint maxContour = Collections.max(contours, Comparator.comparingDouble(Imgproc::contourArea));
//
//            // 过滤面积过小的轮廓（避免噪声干扰）
//            double maxArea = Imgproc.contourArea(maxContour);
//            if (maxArea < 5000) {
//                return "NONE"; // 这里可以调整面积阈值
//            }
//
//            // 4. 计算凸包
//            MatOfInt hull = new MatOfInt();
//            Imgproc.convexHull(maxContour, hull);
//
//            // 5. 计算凸缺陷
//            MatOfInt4 defects = new MatOfInt4();
//            if (hull.toArray().length > 3) {
//                Imgproc.convexityDefects(maxContour, hull, defects);
//            }
//
//            // 6. 计算手指数量
//            int fingerCount = 0;
//            if (!defects.empty()) {
//                fingerCount = countFingers(maxContour, defects);  // 这里传入 maxContour
//            }
//
//            // 绘制调试信息
//            Imgproc.drawContours(debugFrame, contours, contours.indexOf(maxContour), new Scalar(0, 255, 0), 2);
//            Imgproc.putText(debugFrame, "Fingers: " + fingerCount, new Point(10, 30),
//                    Imgproc.FONT_HERSHEY_SIMPLEX, 1, new Scalar(0, 0, 255), 2);
//
//            // 显示调试窗口
//            HighGui.imshow("Threshold", threshold);
//            HighGui.imshow("Contours", debugFrame);
//
//            System.out.println("检测到的手指数量: " + fingerCount);
//            return classifyGesture(fingerCount); // 计算手势
//        }
//
//        /**
//         * 计算手指数量
//         * @param contour  轮廓
//         * @param defects  缺陷
//         * @return
//         */
//        private int countFingers(MatOfPoint contour, MatOfInt4 defects) {
//            int count = 0;
//            Point[] contourArray = contour.toArray();
//            int[] defectArray = defects.toArray();
//
//            for (int i = 0; i < defectArray.length; i += 4) {
//                int startIdx = defectArray[i];    // 起点索引
//                int endIdx = defectArray[i + 1];  // 终点索引
//                int farIdx = defectArray[i + 2];  // 缺陷点索引
//                float depth = defectArray[i + 3] / 256.0f;  // 缺陷深度（归一化）
//
//                Point startPoint = contourArray[startIdx]; // 获取起点、终点、缺陷点
//                Point endPoint = contourArray[endIdx]; // 获取缺陷点
//                Point farPoint = contourArray[farIdx]; // 获取缺陷点
//
//                // 计算手指之间的角度（角度过大的不算）
//                double angle = calculateAngle(startPoint, farPoint, endPoint);
//                if (angle < 90 && depth > 20) {  // 90° 以内的才算手指
//                    count++;
//                }
//            }
//            return count + 1; // 缺陷数 + 1 约等于手指数
//        }
//
//        /**
//         * 计算角度（余弦定理）
//         * @param a
//         * @param b
//         * @param c
//         * @return
//         */
//        private double calculateAngle(Point a, Point b, Point c) {
//            double ab = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
//            double bc = Math.sqrt(Math.pow(b.x - c.x, 2) + Math.pow(b.y - c.y, 2));
//            double ac = Math.sqrt(Math.pow(c.x - a.x, 2) + Math.pow(c.y - a.y, 2));
//
//            return Math.toDegrees(Math.acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)));
//        }
//
//        /**
//         * 手势分类
//         * @param fingers  手指数量
//         * @return
//         */
//        private String classifyGesture(int fingers) {
//            return switch (fingers) {
//                case 0, 1 -> "STOP";  // 握拳或单指
//                case 2 -> "MOVE_LEFT";
//                case 3 -> "MOVE_RIGHT";
//                case 4 -> "MOVE_BACK";
//                case 5 -> "MOVE_FORWARD";
//                default -> "UNKNOWN";
//            };
//        }
//
//        // 获取调试帧
//        public Mat getDebugFrame() {
//            return debugFrame;
//        }
//    }
//
//
//
//    /**
//     * 机器人控制器
//     */
//    static class RobotController {
//
//        // 串口通信实现
//        private final SerialCommunicator serial;
//
//        // 构造函数
//        public RobotController(SerialCommunicator serial) {
//            this.serial = serial;
//        }
//
//        /**
//         * 执行手势控制命令
//         * @param gesture
//         */
//        public void executeCommand(String gesture) {
//            String command = switch (gesture) {
//                case "MOVE_FORWARD" -> "F";// 前进
//                case "MOVE_BACK" -> "B"; // 后退
//                case "MOVE_LEFT" -> "L"; // 左转
//                case "MOVE_RIGHT" -> "R"; // 右转
//                case "STOP" -> "S"; // 停止
//                default -> "";
//            };
//
//            if (!command.isEmpty()) {
//                // 发送控制指令
//                serial.send(command);
//                //System.out.println("执行指令: " + command);
//            }
//        }
//    }
//
//    /**
//     * 串口通信实现
//     */
//    static class SerialCommunicator implements AutoCloseable {
//
//        // 串口对象
//        private SerialPort serialPort;
//
//        /**
//         * 构造函数
//         * @param portName  串口名称
//         * @param baudRate  波特率
//         * @throws Exception
//         */
//        public SerialCommunicator(String portName, int baudRate) throws Exception {
//            // 获取所有可用串口
//            SerialPort[] ports = SerialPort.getCommPorts();
//            if (ports.length == 0) {
//                throw new RuntimeException("未检测到任何可用串口");
//            }
//
//            // 查找目标串口
//            serialPort = Arrays.stream(ports)
//                    .filter(p -> p.getSystemPortName().equalsIgnoreCase(portName))
//                    .findFirst()
//                    .orElseThrow(() -> new RuntimeException("未找到串口: " + portName));
//
//            // 配置串口参数
//            serialPort.setBaudRate(baudRate);
//            serialPort.setNumDataBits(8);
//            serialPort.setNumStopBits(1);
//            serialPort.setParity(SerialPort.NO_PARITY);
//            serialPort.setComPortTimeouts(
//                    SerialPort.TIMEOUT_WRITE_BLOCKING,
//                    1000, // 写入超时
//                    0     // 读取超时（不等待）
//            );
//
//            // 尝试打开端口
//            if (!serialPort.openPort()) {
//                throw new RuntimeException("打开串口失败，可能原因：" +
//                        "\n1. 端口被其他程序占用" +
//                        "\n2. 驱动程序未正确安装" +
//                        "\n3. 权限不足（Linux/Mac需sudo运行）");
//            }
//            System.out.println("成功打开串口: " + portName);
//        }
//
//
//        /**
//         * 发送数据
//         * @param data
//         */
//        public void send(String data) {
//            serialPort.writeBytes(data.getBytes(), data.length());
//        }
//
//        /**
//         * 关闭串口
//         */
//        @Override
//        public void close() {
//            if (serialPort != null) {
//                serialPort.closePort();
//            }
//        }
//    }
//
//    /**
//     * 测试
//     * 手势映射：
//     *  张开手掌（5指）→ 前进
//     *  四指 → 后退
//     *  三指 → 右转
//     *  二指 → 左转
//     *  握拳或单指 → 停止
//     * @param gesture
//     * @return
//     */
//    public String shoushizhongwen(String gesture){
//        String command = switch (gesture) {
//            case "MOVE_FORWARD" -> "前进";
//            case "MOVE_BACK" -> "后退";
//            case "MOVE_LEFT" -> "左转";
//            case "MOVE_RIGHT" -> "右转";
//            case "STOP" -> "停止";
//            default -> "";
//        };
//        return command;
//    }
//
//}




//package com.ai.somatosensoryControl;
//
//import org.opencv.core.*;
//import org.opencv.highgui.HighGui;
//import org.opencv.imgproc.Imgproc;
//import org.opencv.videoio.VideoCapture;
//import com.fazecast.jSerialComm.SerialPort;
//
//import java.util.*;
//
//import org.opencv.videoio.Videoio;
//
///**
// * 手势控制
// *
// * 硬件配置：
// *  支持USB摄像头（分辨率至少640x480）
// *  机器人控制器需支持串口通信（常见波特率9600）
// *
// *
// *  手势识别原理：
// *
// * 图像预处理：灰度转换 → 高斯模糊 → 阈值分割
// *
// * 特征提取：轮廓分析 → 凸包检测 → 凸性缺陷计算
// *
// * 手指计数：通过凸性缺陷深度判断手指数量
// *
// * 手势映射：
// *
// * 张开手掌（5指）→ 前进
// *
// * 四指 → 后退
// *
// * 三指 → 右转
// *
// * 二指 → 左转
// *
// * 握拳 → 停止
// *
// * 此完整实现可直接编译运行，但需注意：
// * 根据实际硬件修改串口名称和波特率
// *
// * 调整光线环境确保手部清晰可见
// *
// * 可能需要根据具体机器人协议修改指令字符（代码中"F/B/L/R/S"）
// *
// * OpenCV本地库配置需正确
// *
// */
//public class FullGestureControl {
//
//    // 串口通信相关
//    private static final String PORT_NAME = "COM3";
//    private static final int BAUD_RATE = 9600;
//
//    // 加载OpenCV库
//    static {
//        // 这里将openCV的dll 文件配置在 D:\InstallationenvironmentHwp\jdk17\bin\opencv_java470.dll 下存放
//        System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // 自动加载 OpenCV 的核心库
//    }
//
//    /**
//     *  主函数
//     * @param args
//     * @throws Exception
//     */
//    public static void main(String[] args) throws Exception {
//        // 启动程序
//        new FullGestureControl().run();
//    }
//
//    /**
//     * 主函数
//     */
//    public void run() {
//
//        // 创建摄像头控制器和串口通信控制器
//        try (CameraController camera = new CameraController();
//             //SerialCommunicator serial = new SerialCommunicator(PORT_NAME, BAUD_RATE)
//        ) {
//
//            // 创建手势处理器和机器人控制器
//            GestureProcessor processor = new GestureProcessor();
//            //RobotController robot = new RobotController(serial);
//
//            System.out.println("系统启动成功 - 开始手势识别");
//
//            // 循环处理摄像头图像和手势识别
//            while (true) {
//                // 捕获摄像头图像
//                Mat frame = camera.captureFrame();
//                // 检查摄像头图像是否为空
//                if (frame.empty()) continue;
//                // 处理摄像头图像并获取手势
//                String gesture = processor.processFrame(frame);
//                // 执行手势控制
//                //robot.executeCommand(gesture);
//                String shoushizhongwen = shoushizhongwen(gesture);
//                if (!"".equals(shoushizhongwen)){
//                    //System.out.println("识别的手势结果：" + shoushizhongwen);
//                }
//                // 显示处理结果（调试用）
//                Mat displayFrame = processor.getDebugFrame();
//                // 显示处理结果
//                HighGui.imshow("Gesture Control", displayFrame);
//                // 等待键盘输入 ESC退出
//                if (HighGui.waitKey(1) == 27) break;
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            // 关闭摄像头窗口
//            HighGui.destroyAllWindows();
//        }
//    }
//
//    /**
//     * 摄像头控制器（带自动关闭）
//     */
//    public class CameraController implements AutoCloseable {
//
//        // 摄像头控制器
//        private final VideoCapture cap;
//
//        /**
//         * 构造函数，初始化摄像头控制器
//         */
//        public CameraController() {
//            // 加载OpenCV库
//            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//            // 创建摄像头控制器
//            cap = new VideoCapture(0);
//            // 检查摄像头是否打开
//            if (!cap.isOpened()) {
//                throw new RuntimeException("无法打开摄像头");
//            }
//            // 设置摄像头参数
//            cap.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
//            cap.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
//        }
//
//        /**
//         * 捕获一帧图像
//         * @return
//         */
//        public Mat captureFrame() {
//            // 创建Mat对象，用于存储捕获的图像
//            Mat frame = new Mat();
//            // 从摄像头捕获一帧图像
//            cap.read(frame);
//            return frame;
//        }
//
//        /**
//         * 关闭摄像头控制器
//         */
//        @Override
//        public void close() {
//            // 释放摄像头资源
//            cap.release();
//        }
//    }
//
//    /**
//     * 手势处理核心 （重点）
//     */
//    static class GestureProcessor {
//
//        // 调试帧
//        private Mat debugFrame;
//
//        /**
//         * 处理一帧图像，返回手势
//         * @param input  输入图像
//         * @return  识别的手势
//         */
//        public String processFrame(Mat input) {
//            // 创建调试帧
//            debugFrame = input.clone();
//
//            // 1. 预处理 - 转换为灰度图像并模糊
//            Mat gray = new Mat();
//            Imgproc.cvtColor(input, gray, Imgproc.COLOR_BGR2GRAY);
//            Imgproc.GaussianBlur(gray, gray, new Size(5, 5), 0);
//
//            // 2. 手部区域分割 - 二值化
//            Mat threshold = new Mat();
//            Imgproc.threshold(gray, threshold, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
//
//            // 3. 轮廓分析 - 寻找轮廓
//            List<MatOfPoint> contours = new ArrayList<>();
//            Mat hierarchy = new Mat();
//            Imgproc.findContours(threshold, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
//
//            // 如果没有找到有效轮廓，返回 "NONE"
//            if (contours.isEmpty()) return "NONE";
//
//            // 找到最大轮廓
//            MatOfPoint maxContour = Collections.max(contours, Comparator.comparingDouble(Imgproc::contourArea));
//
//            // 过滤面积过小的轮廓（避免噪声干扰）
//            double maxArea = Imgproc.contourArea(maxContour);
//            if (maxArea < 5000) {  // 5000 这个值可以根据实际情况调整
//                return "NONE";
//            }
//
//            // 4. 计算凸包
//            MatOfInt hull = new MatOfInt();
//            Imgproc.convexHull(maxContour, hull);
//
//            // 5. 计算凸缺陷
//            MatOfInt4 defects = new MatOfInt4();
//            if (hull.toArray().length > 3) {
//                Imgproc.convexityDefects(maxContour, hull, defects);
//            }
//
//            // 6. 计算手指数量
//            int fingerCount = 0;
//            if (!defects.empty()) {
//                fingerCount = countFingers(maxContour, defects);  // 这里传入 maxContour
//            }
//
//            // 绘制调试信息
//            Imgproc.drawContours(debugFrame, contours, contours.indexOf(maxContour), new Scalar(0, 255, 0), 2);
//            Imgproc.putText(debugFrame, "Fingers: " + fingerCount, new Point(10, 30),
//                    Imgproc.FONT_HERSHEY_SIMPLEX, 1, new Scalar(0, 0, 255), 2);
//
//            // 显示调试窗口
//            HighGui.imshow("Threshold", threshold);
//            HighGui.imshow("Contours", debugFrame);
//
//            // 返回识别的手势
//            System.out.println("================ " + fingerCount + " ====================");
//            return classifyGesture(fingerCount);
//        }
//
//
//
//
//        /**
//         * 计算手指数量
//         * @param contour  轮廓
//         * @param defects  凸缺陷
//         * @return  计算出的手指数量
//         */
//        private int countFingers(MatOfPoint contour, MatOfInt4 defects) {
//            int count = 0;
//            Point[] contourArray = contour.toArray();
//            int[] defectArray = defects.toArray();
//
//            for (int i = 0; i < defectArray.length; i += 4) {
//                int startIdx = defectArray[i];    // 起点索引
//                int endIdx = defectArray[i + 1];  // 终点索引
//                int farIdx = defectArray[i + 2];  // 缺陷点索引
//                float depth = defectArray[i + 3] / 256.0f;  // 缺陷深度（归一化）
//
//                Point startPoint = contourArray[startIdx];
//                Point endPoint = contourArray[endIdx];
//                Point farPoint = contourArray[farIdx];
//
//                // 计算手指之间的角度（角度过大的不算）
//                double angle = calculateAngle(startPoint, farPoint, endPoint);
//                if (angle < 90 && depth > 20) {  // 90° 以内的才算手指
//                    count++;
//                }
//            }
//            return count + 1; // 缺陷数 + 1 约等于手指数
//        }
//
//        /**
//         * 计算角度（余弦定理）
//         */
//        private double calculateAngle(Point a, Point b, Point c) {
//            double ab = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
//            double bc = Math.sqrt(Math.pow(b.x - c.x, 2) + Math.pow(b.y - c.y, 2));
//            double ac = Math.sqrt(Math.pow(c.x - a.x, 2) + Math.pow(c.y - a.y, 2));
//
//            return Math.toDegrees(Math.acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)));
//        }
//
//
//
//        /**
//         * 手势分类
//         *
//         * 张开手掌（5指）→ 前进
//         *
//         * 四指 → 后退
//         *
//         * 三指 → 右转
//         *
//         * 二指 → 左转
//         *
//         * 握拳 → 停止
//         *
//         * @param fingers
//         * @return
//         */
//        private String classifyGesture(int fingers) {
//            return switch (fingers) {
//                case 0, 1 -> "STOP";  // 握拳或单指
//                case 2 -> "MOVE_LEFT";
//                case 3 -> "MOVE_RIGHT";
//                case 4 -> "MOVE_BACK";
//                case 5 -> "MOVE_FORWARD";
//                default -> "UNKNOWN";
//            };
//        }
//
//        /**
//         * 获取调试帧
//         * @return
//         */
//        public Mat getDebugFrame() {
//            return debugFrame;
//        }
//    }
//
//    /**
//     * 机器人控制器
//     */
//    static class RobotController {
//
//        // 串口通信实现
//        private final SerialCommunicator serial;
//
//        // 构造函数
//        public RobotController(SerialCommunicator serial) {
//            this.serial = serial;
//        }
//
//        /**
//         * 执行手势控制命令
//         * @param gesture
//         */
//        public void executeCommand(String gesture) {
//            String command = switch (gesture) {
//                case "MOVE_FORWARD" -> "F";// 前进
//                case "MOVE_BACK" -> "B"; // 后退
//                case "MOVE_LEFT" -> "L"; // 左转
//                case "MOVE_RIGHT" -> "R"; // 右转
//                case "STOP" -> "S"; // 停止
//                default -> "";
//            };
//
//            if (!command.isEmpty()) {
//                // 发送控制指令
//                serial.send(command);
//                System.out.println("执行指令: " + command);
//            }
//        }
//    }
//
//    /**
//     * 串口通信实现
//     */
//    static class SerialCommunicator implements AutoCloseable {
//
//        // 串口对象
//        private SerialPort serialPort;
//
//        /**
//         * 构造函数
//         * @param portName  串口名称
//         * @param baudRate  波特率
//         * @throws Exception
//         */
//        public SerialCommunicator(String portName, int baudRate) throws Exception {
//            // 获取所有可用串口
//            SerialPort[] ports = SerialPort.getCommPorts();
//            if (ports.length == 0) {
//                throw new RuntimeException("未检测到任何可用串口");
//            }
//
//            // 查找目标串口
//            serialPort = Arrays.stream(ports)
//                    .filter(p -> p.getSystemPortName().equalsIgnoreCase(portName))
//                    .findFirst()
//                    .orElseThrow(() -> new RuntimeException("未找到串口: " + portName));
//
//            // 配置串口参数
//            serialPort.setBaudRate(baudRate);
//            serialPort.setNumDataBits(8);
//            serialPort.setNumStopBits(1);
//            serialPort.setParity(SerialPort.NO_PARITY);
//            serialPort.setComPortTimeouts(
//                    SerialPort.TIMEOUT_WRITE_BLOCKING,
//                    1000, // 写入超时
//                    0     // 读取超时（不等待）
//            );
//
//            // 尝试打开端口
//            if (!serialPort.openPort()) {
//                throw new RuntimeException("打开串口失败，可能原因：" +
//                        "\n1. 端口被其他程序占用" +
//                        "\n2. 驱动程序未正确安装" +
//                        "\n3. 权限不足（Linux/Mac需sudo运行）");
//            }
//            System.out.println("成功打开串口: " + portName);
//        }
//
//
//        /**
//         * 发送数据
//         * @param data
//         */
//        public void send(String data) {
//            serialPort.writeBytes(data.getBytes(), data.length());
//        }
//
//        /**
//         * 关闭串口
//         */
//        @Override
//        public void close() {
//            if (serialPort != null) {
//                serialPort.closePort();
//            }
//        }
//    }
//
//    /**
//     * 测试
//     * 手势映射：
//     *  张开手掌（5指）→ 前进
//     *  四指 → 后退
//     *  三指 → 右转
//     *  二指 → 左转
//     *  握拳或单指 → 停止
//     * @param gesture
//     * @return
//     */
//    public String shoushizhongwen(String gesture){
//        String command = switch (gesture) {
//            case "MOVE_FORWARD" -> "前进";
//            case "MOVE_BACK" -> "后退";
//            case "MOVE_LEFT" -> "左转";
//            case "MOVE_RIGHT" -> "右转";
//            case "STOP" -> "停止";
//            default -> "";
//        };
//        return command;
//    }
//
//}